<template>
  <div class="p-5">
    <!-- <div>AI智能体集群</div> -->
    <div class="flex gap-3 items-stretch" style="height: calc(100vh - 100px)">
      <!-- 左侧 -->
      <div class="left-wrapper flex-none overflow-auto px-3">
        <div
          v-for="(item, index) in baseList"
          :key="index"
          class="flex p-3 card-list-container my-2 gap-3 cursor-pointer"
          :class="{ 'base-active': activeIndexBase === index }"
          @click="handleClickBase(item, index)"
        >
          <div class="flex-none w-10 flex items-center justify-center">
            <img :src="index ? getImageUrl(item.icon) : item.icon" alt="" width="46" />
          </div>

          <div class="text-black text-sm">
            <div class="text-base" :style="{ color: item.color || colors[index] }">
              {{ item.name }}
            </div>
            <div class="text-xs mt-1.5">{{ item.remark }}</div>
          </div>
        </div>
      </div>
      <!-- 中心 -->
      <div class="middle-wrapper w-2 flex-1 flex flex-col justify-between">
        <div class="relative">
          <div class="flex items-center justify-between">
            <div class="flex p-3 my-2 gap-3">
              <div class="flex-none w-10 flex items-center justify-center">
                <img
                  :src="activeIndexBase ? getImageUrl(activeBase.icon) : activeBase.icon"
                  alt=""
                  width="46"
                />
              </div>

              <div class="text-black">
                <div class="text-base font-extrabold" :style="{ color: colors[activeIndexBase] }">
                  {{ activeBase.name }}
                </div>
                <div class="mt-1.5">{{ activeBase.remark }}</div>
              </div>
            </div>
            <div class="flex-none w-28">
              <el-select v-model="dialogChartParams.model">
                <el-option label="快速推理" value="deepseek-r1:14b" />
                <el-option label="深度推理" value="deepseek-r1:70b" />
              </el-select>
            </div>
          </div>

          <!-- 分页 -->
          <div class="absolute top-16 right-10" v-if="infoPageTotal > 1">
            <div class="flex items-center" style="color: #41a3ff">
              <el-icon
                class="cursor-pointer px-0.5"
                :class="[infoPageParams.pageNum === 1 ? 'cursor-not-allowed' : 'cursor-pointer']"
                @click="handlePre(infoPageParams.pageNum)"
              >
                <ArrowLeft />
              </el-icon>
              <span>{{ infoPageParams.pageNum }}</span>
              /
              <span>{{ infoPageTotal }}</span>
              <el-icon
                class="px-0.5"
                :class="[
                  infoPageParams.pageNum === infoPageTotal
                    ? 'cursor-not-allowed'
                    : 'cursor-pointer',
                ]"
                @click="handleNext(infoPageParams.pageNum)"
              >
                <ArrowRight />
              </el-icon>
            </div>
          </div>
          <!-- 会话列表 -->
          <div ref="dialogListRef" class="overflow-auto relative" style="max-height: 600px">
            <div v-for="(item, index) in infoList" :key="index">
              <!-- 提问 user -->
              <div class="flex justify-end items-start gap-2 my-2 p-2" v-if="item.role === 'user'">
                <div class="mr-1 flex-1 ml-9">
                  <div class="float-right q-container">
                    {{ item.originalContent }}
                  </div>
                </div>
                <img src="@/assets/images/ai/sence-user.png" alt="" width="26" />
              </div>
              <!-- 回答 -->
              <div class="flex items-start gap-2 my-2 p-2" v-else>
                <template v-if="index === infoList.length - 1 && !isStopAnswer">
                  <img src="@/assets/images/loading.gif" alt="" width="26" />
                </template>
                <template v-else>
                  <img src="@/assets/images/ansewer.png" alt="" width="26" />
                </template>
                <div class="ml-3 mr-9 flex-1">
                  <div v-if="formatThinkData(item.thinkData)">
                    <div class="text-sm flex flex-row items-center mb-2" style="color: #6e6e6e">
                      <span class="mr-4">已深度思考</span>
                      <span
                        class="cursor-pointer"
                        @click="item.thinkDataShow = !item.thinkDataShow"
                      >
                        <el-icon v-if="item.thinkDataShow"><ArrowUp /></el-icon>
                        <el-icon v-else><ArrowDown /></el-icon>
                      </span>
                    </div>
                    <div
                      v-show="item.thinkDataShow"
                      style="
                        padding-left: 10px;
                        border-left: 2px solid #939292;
                        color: #6e6e6e;
                        font-size: 14px;
                      "
                      v-html="setHtmlMarked(item.thinkData)"
                    ></div>
                  </div>
                  <div class="a-container">
                    <div class="">
                      <div class="inline-block" v-html="setHtmlMarked(item.originalContent)"></div>
                      <span class="text-4xl" v-if="index === infoList.length - 1 && !isStopAnswer">
                        .
                      </span>
                    </div>
                    <div v-if="isStopAnswer && item.docs?.length">
                      <div class="line my-2" style="border-bottom: 1px solid #fff"></div>
                      <div>
                        本次回复内容由福政通生成，仅供参考，如需查看详情可点击一下相关链接：
                      </div>
                      <div
                        v-for="(docInfo, dindex) in item.docs"
                        :key="dindex"
                        class="my-1.5 cursor-pointer"
                        @click="handleClickDocName(docInfo)"
                      >
                        {{ docInfo.idx }}、
                        {{ docInfo.docName }}
                        <!-- <span v-html="setHtmlMarked(docInfo.content)"></span> -->
                      </div>
                    </div>
                  </div>
                </div>
              </div>
              <template
                v-if="
                  index === infoList.length - 1 &&
                  infoList[infoList.length - 1].role === 'assistant'
                "
              >
                <div
                  class="cursor-pointer ml-14"
                  style="color: #4955f5"
                  v-if="!isStopAnswer"
                  @click="handleStopChart"
                >
                  停止生成
                </div>
                <!-- <div
                  class="cursor-pointer ml-14"
                  style="color: #4955f5"
                  v-else
                  @click="handleRenewChart"
                >
                  重新生成
                </div> -->
              </template>
            </div>
          </div>
        </div>
        <div class="relative">
          <el-input
            ref="inputRef"
            class="big-input"
            v-model="dialogChartParams.content"
            @keyup.enter="handleSend"
            placeholder="输入你的问题"
          />
          <div class="absolute right-6 bottom-4 cursor-pointer" @click="handleSend">
            <img src="@/assets/images/send.png" alt="" width="28" />
          </div>
        </div>
      </div>
      <!-- 右侧 -->
      <div class="right-wrapper flex flex-col gap-2">
        <div class="card-list-container p-4 flex-1 h-full">
          <div class="flex justify-between pr-3 cursor-pointer mb-4" @click="handleNewAdd">
            <div class="flex items-center gap-3">
              <img src="@/assets/images/ai/topic-icon.png" alt="" />
              <div>历史对话</div>
            </div>
            <div class="flex items-center" style="color: #409eff">
              <el-icon color="#409eff"><Plus /></el-icon>新建对话
            </div>
          </div>
          <div class="text-xs pr-3 his-container">
            <div
              v-for="(item, index) in historyList"
              :key="index"
              class="gap-2 my-2 px-4 py-2.5 cursor-pointer his-item-container"
              :class="{ active: item.id === currentHistoryStory.id }"
              @click="handleHistoryDetail(item)"
            >
              <div class="flex items-center gap-2">
                <img src="@/assets/images/ai/time-icon.png" alt="" width="16" />
                <div style="color: #666">{{ item.createTime }}</div>
              </div>
              <div class="mt-3 text-white px-4 py-2" style="background-color: #5ca4ff">
                {{ item.firstContent }}
              </div>
            </div>
          </div>
          <pagination
            v-show="historyTotal > 0"
            :total="historyTotal"
            v-model:page="pageHisparams.pageNum"
            v-model:limit="pageHisparams.pageSize"
            layout="total, prev, pager, next"
            @pagination="getSessionDialog"
          />
        </div>
      </div>
    </div>

    <el-dialog v-model="docDialog.visible" center :title="docDialog.data.docName">
      <div
        class="px-3 text-base overflow-auto"
        style="max-height: 600px"
        v-html="setDialogHtml(docDialog.data.content)"
      ></div>
    </el-dialog>
  </div>
</template>

<script setup>
  import { chartCommon, pageChatHistory, pageChatSession } from '@/api/intelligencebase/aiApply';
  import { pageSdkLmScene } from '@/api/intelligencebase/sdkLmScene';
  import senceImg1 from '@/assets/images/ai/sence-icon1.png';
  import cache from '@/plugins/cache';
  import modal from '@/plugins/modal';
  import { getToken } from '@/utils/auth';
  import DOMPurify from 'dompurify';
  import { marked } from 'marked';
  import { computed, onMounted, ref, watch } from 'vue';

  let controller;
  let timer;
  const colors = ['#B491FF', '#FE3E57', '#FE8F19', '#CB8E51', '#599FFF', '#CE55B1'];
  const baseList = ref([
    {
      icon: senceImg1,
      name: '通用智能体',
      remark: '政务服务办事指南，流程咨询，资格测评',
      color: '#B491FF',
    },
  ]);
  const activeIndexBase = ref(0);

  const loading = ref(false);
  const historyList = ref([
    // { firstContent: '开设参观流程咨询', createTime: '10-04 17:55' },
    // { firstContent: '初中、小学、幼儿园教师资格认定流程', createTime: '10-04 17:55' },
    // { firstContent: '教师资格证申请攻略', createTime: '10-04 17:55' },
  ]);
  const historyTotal = ref(0);
  const pageHisparams = ref({
    pageNum: 1,
    pageSize: 10,
  });
  const dialogListRef = ref(null);
  const dialogChartParams = ref({
    content: null,
    sessionId: null, //"会话id（第一次不传，如果没传，返回里会有sessionId）",
    sceneCode: null, // "场景编码，（传则为智库对话，否则为普通对话）"
    model: 'deepseek-r1:14b', // 14b的为快速推理，70b的为深度推理  deepseek-r1:14b，deepseek-r1:70b
  });
  const inputRef = ref(null);
  const infoList = ref([]);
  const currentHistoryStory = ref({}); //当前选中的历史会话
  const infoPageTotal = ref(1);
  const infoPageParams = ref({
    pageNum: 1,
    pageSize: 10,
  });

  const currentContent = ref({
    content: '',
    role: '',
    docs: [],
  });
  const currentContentLen = ref(0);
  const isStopAnswer = ref(true);
  const currentIndex = ref(0);
  const docDialog = ref({
    visible: false,
    data: {},
  });

  const activeBase = computed(() => {
    return baseList.value[activeIndexBase.value];
  });
  const createSenceParams = computed(() => {
    return {
      sceneCode: activeBase.value.code || null, // "场景编码，（传则为智库对话，否则为普通对话）"
      sceneId: activeBase.value.id || null, // "场景id"
    };
  });
  // 点击 引用 显示弹窗
  function handleClickDocName(info) {
    docDialog.value = {
      visible: true,
      data: info,
    };
  }
  // 设置图片url
  function getImageUrl(url) {
    const baseUrl = import.meta.env.VITE_APP_BASE_API;
    if (!url) return;
    return baseUrl + url + `?accessToken=${getToken()}`;
  }
  // md设置html内容
  function setHtmlMarked(content) {
    return DOMPurify.sanitize(marked(content));
  }
  // 弹窗 文件内容 格式化
  function setDialogHtml(content) {
    const text = content.replace(/\n/g, '<br />');
    return DOMPurify.sanitize(marked(text));
  }
  // 获取场景列表
  function getList() {
    loading.value = true;
    pageSdkLmScene({ pageNum: 1, pageSize: 999 }, { enable: true })
      .then((res) => {
        baseList.value = [
          {
            icon: senceImg1,
            name: '通用智能体',
            remark: '政务服务办事指南，流程咨询，资格测评',
            color: '#B491FF',
          },
          ...(res?.data?.records ?? []),
        ];
      })
      .finally(() => {
        loading.value = false;
      });
  }
  function handleClickBase(row, index) {
    console.log({ row });
    activeIndexBase.value = index;
    dialogChartParams.value = {
      content: null,
      sessionId: null, //"会话id（第一次不传，如果没传，返回里会有sessionId）",
      sceneCode: null, // "场景编码，（传则为智库对话，否则为普通对话）",
      model: 'deepseek-r1:14b', // 14b的为快速推理，70b的为深度推理  deepseek-r1:14b，deepseek-r1:70b
      ...createSenceParams.value,
    };
    currentHistoryStory.value = {};
    infoList.value = [];
    infoPageTotal.value = 1;
    infoPageParams.value.pageNum = 1;
    getSessionDialog();
  }
  // 发送问题 按钮事件
  const handleSend = function () {
    if (!isStopAnswer.value) {
      modal.msgInfo('稍等片刻，等智能体回复后再发送哦');
      return;
    }
    receiveMessage();
  };
  // 新建会话
  function handleNewAdd() {
    stopChartContent();
    dialogChartParams.value = {
      content: null,
      sessionId: null, //"会话id（第一次不传，如果没传，返回里会有sessionId）",
      sceneCode: null, // "场景编码，（传则为智库对话，否则为普通对话）",
      ...createSenceParams.value,
    };
    cache.session.remove('sessionId');
    inputRef.value.focus();
    infoList.value = [];
    infoPageTotal.value = 1;
    infoPageParams.value.pageNum = 1;
  }

  // 历史会话列表
  async function getSessionDialog() {
    const res = await pageChatSession({ ...pageHisparams.value }, createSenceParams.value);
    const { records, total } = res.data;
    historyList.value = records;
    historyTotal.value = total;
  }

  function formatThinkData(s = '') {
    return s.trim();
  }

  function receiveMessage() {
    console.log({ dialogChartParams: dialogChartParams.value });
    infoList.value.push({ originalContent: dialogChartParams.value.content, role: 'user' });
    infoList.value.push({
      originalContent: '',
      thinkData: '',
      thinkDataShow: true,
      role: 'assistant',
    });

    isStopAnswer.value = false;
    currentContentLen.value = 0;
    getScrollBottom();
    chartCommon(dialogChartParams.value, {
      signal: controller?.signal,
      mode: 'cors',
      openWhenHidden: true,
      onmessage(event) {
        console.log({ data: event.data });
        const data = event.data;
        getFormatChartData(JSON.parse(data));
      },
      async onopen(response) {
        if (response.ok) {
          console.log('开始建立连接', response);
          dialogChartParams.value.content = null;
        }
      },
      onclose() {
        console.log('会话结束');
        currentContentLen.value = currentContent.value.content.length;
        stopChartContent();
      },
      onerror(err) {
        console.log({ err });
        stopChartContent();
      },
    });
  }
  function getFormatChartData(res) {
    const { msgType, data } = res;
    console.log({ res, msgType, sessionId: dialogChartParams.value.sessionId });
    if (msgType === 'chatSession') {
      // 会话第一次创建 返回session
      dialogChartParams.value.sessionId = data.sessionId;
    } else if (msgType === 'chatError') {
      // 出错
      modal.msgError(data);
      stopChartContent();
    } else if (msgType === 'chatData') {
      // 返回问答内容
      const arrlen = infoList.value.length;
      infoList.value[arrlen - 1].originalContent += data.content;
      infoList.value[arrlen - 1].role = data.role;
      getScrollBottom();
    } else if (msgType === 'chatThink') {
      const arrlen = infoList.value.length;
      infoList.value[arrlen - 1].thinkData += data.content;
      infoList.value[arrlen - 1].role = data.role;
      getScrollBottom();
    } else if (msgType === 'chatDocs') {
      // 返回 出处
      console.log({ data });
      // currentContent.value.docs=data
      infoList.value[infoList.value.length - 1].docs = data;
    }
  }

  function getScrollBottom(delay = 0) {
    if (timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(() => {
      dialogListRef.value.scrollTo({
        top: dialogListRef.value.scrollHeight,
        behavior: 'smooth',
      });
    }, delay);
  }

  //停止生成
  function handleStopChart() {
    stopChartContent();
  }
  // //重新生成
  // function handleRenewChart() {}
  // 历史列表行点击
  function handleHistoryDetail(item) {
    currentHistoryStory.value = item;
    dialogChartParams.value.sessionId = item.sessionId;
    getHistoryDetailList();
  }
  // 历史列表分页 上一页
  function handlePre(pageNum) {
    if (pageNum === 1) {
      // 是否是第一页
      return;
    }
    infoPageParams.value.pageNum = pageNum - 1;
    const realPage = infoPageTotal.value + 1 - (pageNum - 1);
    console.log({ realPage });
    getHistoryDetailList({ pageNum: realPage });
  }
  // 历史列表分页 下一页
  function handleNext(pageNum) {
    if (pageNum === infoPageTotal.value) {
      // 是否是最后一页
      return;
    }
    infoPageParams.value.pageNum = pageNum + 1;
    const realPage = infoPageTotal.value + 1 - (pageNum + 1);
    getHistoryDetailList({ pageNum: realPage });
  }
  // 获取历史会话 详情列表
  function getHistoryDetailList(query) {
    const params = {
      sessionId: currentHistoryStory.value.sessionId,
      ...query,
    };
    pageChatHistory(params).then((res) => {
      console.log({ res });
      const { records, pages, current } = res.data;
      infoList.value = records.reverse();

      infoList.value.forEach((item) => {
        const s = item.originalContent || '';
        const r = /<think>([\S\s]*)<\/think>/g;
        const m = r.exec(s);
        if (m) {
          item.thinkData = m[1];
          item.thinkDataShow = true;
          item.originalContent = s.replace(r, '');
        }
      });

      infoPageTotal.value = pages;
      infoPageParams.value.pageNum = pages - current + 1;

      getScrollBottom();
    });
  }
  // 停止流生成
  function stopChartContent() {
    controller.abort();
    controller = new AbortController();
    currentIndex.value = 0;
    currentContent.value = {
      role: '',
      content: '',
    };
    isStopAnswer.value = true;
    getScrollBottom();
  }
  watch(
    () => currentContent.value.content,
    (val) => {
      if (!val) return;
      joinContent();
    },
  );
  function joinContent() {
    if (isStopAnswer.value) {
      currentContent.value = { role: '', content: '' };
      currentIndex.value = 0;
      return;
    }
    const q = currentContent.value.content;
    const arrlen = infoList.value.length;
    console.log({ currentIndex: currentIndex.value });
    for (let i = currentIndex.value; i < q.length; i++) {
      setTimeout(() => {
        console.log(2222, { i });
        infoList.value[arrlen - 1].originalContent += q[i];
        getScrollBottom(20);
      }, 50 * i);
      currentIndex.value += 1;
      console.log({ i });
    }
  }
  getSessionDialog();

  onMounted(() => {
    getList();
    controller = new AbortController();
    inputRef.value.focus();
  });
</script>

<style lang="scss" scoped>
  .card-list-container {
    background: #fff;
    box-shadow: 3px 2px 5px 0 #e0e0e0;
  }

  .base-active {
    background-color: rgb(64 158 255 / 10%);
  }

  .left-wrapper {
    width: 300px;
    height: 100%;
    overflow: auto;
    background: #fff;
  }

  .right-wrapper {
    width: 400px;
    height: 100%;
    overflow: hidden;
  }

  .question-container {
    width: auto;
    min-width: 200px;
    max-width: 400px;
  }

  .q-container {
    display: inline-block;
    width: auto;
    max-width: 100%;
    padding: 8px 16px;
    border-radius: 6px;
    background-color: #fff;
  }

  .a-container {
    display: inline-block;
    width: auto;
    max-width: 100%;
    padding: 8px 16px;
    border-radius: 6px;
    background-color: #3b93f3;
    color: #fff;
    // max-height: 530px;
    // overflow: auto;
  }

  .his-container {
    max-height: calc(100% - 70px);
    overflow: auto;
  }

  .his-item-container {
    border-radius: 4px;

    &.active {
      background-color: #e9effb;
    }
  }

  .pagination-container {
    height: 40px;
    margin-top: 0;
  }
</style>
